#pragma once

#ifndef LCD154_HPP__
#define LCD154_HPP__

#include <array>
#include <cmath>
#include <cstdint>
#include <format>
#include <limits>
#include <ostream>

#include "gpio.h"
#include "spi.h"

constexpr uint8_t operator""_u8(unsigned long long value)
{
    return static_cast<uint8_t>(value); // 确保值在0-255范围内
}

constexpr uint16_t operator""_u16(unsigned long long value)
{
    return static_cast<uint16_t>(value); // 确保值在0-65535范围内
}

constexpr std::byte operator""_byte(unsigned long long value)
{
    return static_cast<std::byte>(value); // 确保值在0-255范围内
}

// Point 类
namespace LCD
{
    struct Point {
        std::int16_t x;
        std::int16_t y;

        // 构造/赋值
        constexpr Point(std::int16_t x, std::int16_t y) noexcept;
        constexpr Point() noexcept;

        constexpr Point(const Point &) noexcept            = default;
        constexpr Point(Point &&) noexcept                 = default;
        constexpr Point &operator=(const Point &) noexcept = default;
        constexpr Point &operator=(Point &&) noexcept      = default;

        // 运算符
        constexpr bool         operator==(Point other) const noexcept;
        constexpr bool         operator!=(Point other) const noexcept;
        constexpr Point        operator+(Point other) const noexcept;
        constexpr Point        operator-() const noexcept;
        constexpr Point        operator-(Point other) const noexcept;
        constexpr Point       &operator+=(Point other) noexcept;
        constexpr Point       &operator-=(Point other) noexcept;
        constexpr Point        operator*(std::int16_t scale) const noexcept;
        constexpr Point       &operator*=(std::int16_t scale) noexcept;
        constexpr Point        operator/(std::int16_t scale) const noexcept;
        constexpr Point       &operator/=(std::int16_t scale) noexcept;
        friend constexpr Point operator*(std::int16_t scale, Point point) noexcept;

        // 实用函数
        constexpr bool       within(Point topLeft, Point bottomRight) const noexcept;
        constexpr double     distance(Point other) const noexcept;
        friend std::ostream &operator<<(std::ostream &os, Point point) noexcept;
        std::string          to_string() const noexcept;

        // 静态工厂函数
        static constexpr Point Origin() noexcept;
    };

}

// Point 类实现
namespace LCD
{
    constexpr Point::Point(std::int16_t x, std::int16_t y) noexcept
        : x(x), y(y)
    {
    }
    constexpr Point::Point() noexcept
        : Point(0, 0)
    {
    }
    constexpr bool Point::operator==(Point other) const noexcept
    {
        return x == other.x and y == other.y;
    }
    constexpr bool Point::operator!=(Point other) const noexcept
    {
        return not(*this == other);
    }
    constexpr Point Point::operator+(Point other) const noexcept
    {
        constexpr auto max = std::numeric_limits<std::int16_t>::max();
        constexpr auto min = std::numeric_limits<std::int16_t>::min();

        return {
            static_cast<std::int16_t>(std::clamp<int32_t>(x + other.x, min, max)),
            static_cast<std::int16_t>(std::clamp<int32_t>(y + other.y, min, max))};
    }
    constexpr Point Point::operator-() const noexcept
    {
        return *this * -1;
    }
    constexpr Point Point::operator-(Point other) const noexcept
    {
        return *this + (-other);
    }
    constexpr Point &Point::operator+=(Point other) noexcept
    {
        return *this = *this + other;
    }
    constexpr Point &Point::operator-=(Point other) noexcept
    {
        return *this += (-other);
    }
    constexpr Point Point::operator*(std::int16_t scale) const noexcept
    {
        constexpr auto max = std::numeric_limits<std::int16_t>::max();
        constexpr auto min = std::numeric_limits<std::int16_t>::min();

        return {
            static_cast<std::int16_t>(std::clamp(
                static_cast<std::int32_t>(x) * scale,
                static_cast<std::int32_t>(min),
                static_cast<std::int32_t>(max))),
            static_cast<std::int16_t>(std::clamp(
                static_cast<std::int32_t>(y) * scale,
                static_cast<std::int32_t>(min),
                static_cast<std::int32_t>(max)))};
    }
    constexpr Point &Point::operator*=(std::int16_t scale) noexcept
    {
        return *this = *this * scale;
    }
    constexpr Point Point::operator/(std::int16_t scale) const noexcept
    {
        return {static_cast<std::int16_t>(std::round(static_cast<double>(x) / scale)),
                static_cast<std::int16_t>(std::round(static_cast<double>(y) / scale))};
    }
    constexpr Point &Point::operator/=(std::int16_t scale) noexcept
    {
        x /= scale;
        y /= scale;
        return *this;
    }
    constexpr Point operator*(std::int16_t scale, Point point) noexcept
    {
        return point * scale;
    }
    constexpr bool Point::within(Point topLeft, Point bottomRight) const noexcept
    {
        return (x >= topLeft.x) and
               (x <= bottomRight.x) and
               (y >= topLeft.y) and
               (y <= bottomRight.y);
    }
    constexpr double Point::distance(Point other) const noexcept
    {
        return std::sqrt(
            std::pow(x - other.x, 2) + std::pow(y - other.y, 2));
    }

    constexpr Point Point::Origin() noexcept
    {
        return {};
    }
}

// RGB 颜色类
namespace LCD
{
    struct RGB888 {
        std::uint8_t R;
        std::uint8_t G;
        std::uint8_t B;

        constexpr RGB888(std::uint8_t r, std::uint8_t g, std::uint8_t b) noexcept;
        constexpr RGB888(std::uint32_t color) noexcept;
        constexpr RGB888() noexcept;

        constexpr RGB888(const RGB888 &) noexcept            = default;
        constexpr RGB888(RGB888 &&) noexcept                 = default;
        constexpr RGB888 &operator=(const RGB888 &) noexcept = default;
        constexpr RGB888 &operator=(RGB888 &&) noexcept      = default;

        constexpr std::uint32_t value() const noexcept;

        constexpr bool operator==(RGB888 other) const noexcept;
        constexpr bool operator!=(RGB888 other) const noexcept;
    };

    struct RGB565 {
        std::uint8_t R : 5;
        std::uint8_t G : 6;
        std::uint8_t B : 5;

        // 构造/赋值
        constexpr RGB565(std::uint8_t r, std::uint8_t g, std::uint8_t b) noexcept;
        constexpr RGB565(std::uint16_t value) noexcept;
        constexpr RGB565() noexcept;

        constexpr RGB565(const RGB565 &) noexcept            = default;
        constexpr RGB565(RGB565 &&) noexcept                 = default;
        constexpr RGB565 &operator=(const RGB565 &) noexcept = default;
        constexpr RGB565 &operator=(RGB565 &&) noexcept      = default;

        // 允许使用 RGB888 构造 RGB565
        constexpr RGB565(RGB888 color) noexcept;
        // 允许使用 RGB888 赋值给 RGB565
        constexpr RGB565 &operator=(RGB888 color) noexcept;

        // 运算符
        constexpr bool operator==(RGB565 other) const noexcept;
        constexpr bool operator!=(RGB565 other) const noexcept;

        // 实用函数
        constexpr std::uint16_t value() const noexcept;
    };

}

// RGB 颜色类实现
namespace LCD
{
    // RGB888 成员函数实现
    constexpr RGB888::RGB888(std::uint8_t r, std::uint8_t g, std::uint8_t b) noexcept
        : R(r), G(g), B(b)
    {
    }
    constexpr RGB888::RGB888(std::uint32_t color) noexcept
        : R(color >> 16), G((color >> 8) & 0xFF), B(color & 0xFF)
    {
    }
    constexpr RGB888::RGB888() noexcept
        : RGB888(0, 0, 0)
    {
    }

    constexpr std::uint32_t RGB888::value() const noexcept
    {
        return (R << 16) | (G << 8) | B;
    }

    constexpr bool RGB888::operator==(RGB888 other) const noexcept
    {
        return (R == other.R) and
               (G == other.G) and
               (B == other.B);
    }
    constexpr bool RGB888::operator!=(RGB888 other) const noexcept
    {
        return not(*this == other);
    }

    // RGB565 成员函数实现
    constexpr RGB565::RGB565(std::uint8_t r, std::uint8_t g, std::uint8_t b) noexcept
        : R(r & 0x1F), G(g & 0x3F), B(b & 0x1F)
    {
    }
    constexpr RGB565::RGB565() noexcept
        : RGB565(0, 0, 0)
    {
    }
    constexpr RGB565::RGB565(std::uint16_t value) noexcept
        : RGB565((value >> 11) & 0x1F, (value >> 5) & 0x3F, value & 0x1F)
    {
    }

    constexpr RGB565::RGB565(RGB888 color) noexcept
        : RGB565(color.R >> 3, color.G >> 2, color.B >> 3)
    {
    }
    constexpr RGB565 &RGB565::operator=(RGB888 color) noexcept
    {
        R = color.R >> 3;
        G = color.G >> 2;
        B = color.B >> 3;
        return *this;
    }

    constexpr bool RGB565::operator==(RGB565 other) const noexcept
    {
        return R == other.R and G == other.G and B == other.B;
    }
    constexpr bool RGB565::operator!=(RGB565 other) const noexcept
    {
        return not(*this == other);
    }
    constexpr std::uint16_t RGB565::value() const noexcept
    {
        return static_cast<std::uint16_t>(R) << 11 |
               static_cast<std::uint16_t>(G) << 5 |
               static_cast<std::uint16_t>(B);
    }
}

// RGB 颜色预定义
namespace LCD
{
    constexpr auto WHITE = RGB888(0xFFFFFF); // 纯白色
    constexpr auto BLACK = RGB888(0x000000); // 纯黑色

    constexpr auto BLUE    = RGB888(0x0000FF); // 纯蓝色
    constexpr auto GREEN   = RGB888(0x00FF00); // 纯绿色
    constexpr auto RED     = RGB888(0xFF0000); // 纯红色
    constexpr auto CYAN    = RGB888(0x00FFFF); // 蓝绿色
    constexpr auto MAGENTA = RGB888(0xFF00FF); // 紫红色
    constexpr auto YELLOW  = RGB888(0xFFFF00); // 黄色
    constexpr auto GREY    = RGB888(0x2C2C2C); // 灰色

    constexpr auto LIGHT_BLUE    = RGB888(0x8080FF); // 亮蓝色
    constexpr auto LIGHT_GREEN   = RGB888(0x80FF80); // 亮绿色
    constexpr auto LIGHT_RED     = RGB888(0xFF8080); // 亮红色
    constexpr auto LIGHT_CYAN    = RGB888(0x80FFFF); // 亮蓝绿色
    constexpr auto LIGHT_MAGENTA = RGB888(0xFF80FF); // 亮紫红色
    constexpr auto LIGHT_YELLOW  = RGB888(0xFFFF80); // 亮黄色
    constexpr auto LIGHT_GREY    = RGB888(0xA3A3A3); // 亮灰色

    constexpr auto DARK_BLUE    = RGB888(0x000080); // 暗蓝色
    constexpr auto DARK_GREEN   = RGB888(0x008000); // 暗绿色
    constexpr auto DARK_RED     = RGB888(0x800000); // 暗红色
    constexpr auto DARK_CYAN    = RGB888(0x008080); // 暗蓝绿色
    constexpr auto DARK_MAGENTA = RGB888(0x800080); // 暗紫红色
    constexpr auto DARK_YELLOW  = RGB888(0x808000); // 暗黄色
    constexpr auto DARK_GREY    = RGB888(0x404040); // 暗灰色

}

// 图片类
namespace LCD
{
    template <std::uint16_t Width, std::uint16_t Height>
    struct MonoImage {
        std::array<std::uint8_t, (Width * Height + 7) / 8> data;

        constexpr MonoImage() noexcept = default;
        constexpr MonoImage(const decltype(data) &data) noexcept
            : data(data)
        {
        }

        constexpr std::uint16_t width() const noexcept
        {
            return Width;
        }
        constexpr std::uint16_t height() const noexcept
        {
            return Height;
        }

        constexpr bool operator[](int x, int y) const noexcept
        {
            // 边界检查
            if (x >= Width or y >= Height) return false;

            // 计算位索引和偏移
            const std::size_t  bit_index  = y * Width + x;
            const std::size_t  byte_index = bit_index / 8;
            const std::uint8_t bit_offset = bit_index % 8;

            // 正确提取位
            return (data[byte_index] & (1u << bit_offset)) != 0;
        }
    };

    template <std::uint16_t Width, std::uint16_t Height>
    class ColorImage
    {
    private:
        std::array<std::uint16_t, Width * Height> data_;

    public:
        constexpr ColorImage() noexcept = default;
        constexpr ColorImage(const decltype(data_) &data) noexcept
            : data_(data)
        {
        }

        constexpr std::uint16_t width() const noexcept
        {
            return Width;
        }
        constexpr std::uint16_t height() const noexcept
        {
            return Height;
        }

        constexpr std::uint16_t operator[](int x, int y) const noexcept
        {
            return data_[y * Width + x];
        }

        constexpr const std::uint16_t *data(void) const noexcept
        {
            return data_.data();
        }

        constexpr std::size_t size(void) const noexcept
        {
            return data_.size();
        }
    };
}

// TFTLCD 屏幕类
namespace LCD
{
    enum class DisplayDirection : std::uint8_t {
        Horizontal = 0x00U, // 水平方向
        HorizontalFlip,     // 水平翻转
        Vertical,           // 垂直方向
        VerticalFlip        // 垂直翻转
    };

    class TFTLCD
    {
    public:
        static constexpr std::uint16_t ScreenWidth  = 240;
        static constexpr std::uint16_t ScreenHeight = 240;
        static constexpr std::size_t   BufferSize   = 1024 * 16;

        TFTLCD(SPI_HandleTypeDef &hspi);

        // 初始化设置
        void setBackLightPin(GPIO_TypeDef *port, std::uint16_t pin);
        void setCSPin(GPIO_TypeDef *port, std::uint16_t pin);
        void setDCPin(GPIO_TypeDef *port, std::uint16_t pin);
        void init(void);

        // 屏幕画笔控制函数
        void setDisplayDirection(DisplayDirection direction);
        void setDrawArea(Point topLeft, Point bottomRight);
        void setColor(RGB565 color);
        void setBackColor(RGB565 color);

        // 2D 绘图函数
        void clear(Point topLeft, Point bottomRight);
        void clear(void);

        void drawPoint(Point point);
        void drawLine(Point start, Point end);
        void drawCircle(Point center, std::uint16_t radius);
        void drawRectangle(Point topLeft, Point bottomRight);

        void fillRectangle(Point topLeft, Point bottomRight);
        void fillCircle(Point center, std::uint16_t radius);

        template <std::uint16_t Width, std::uint16_t Height>
        void drawPicture(const MonoImage<Width, Height> &image, Point topLeft = Point::Origin())
        {
            // 计算并设置显示区域
            const auto bottomRight = Point(Width - 1, Height - 1) + topLeft;
            setDrawArea(topLeft, bottomRight);

            // 计算图片尺寸、缓冲区大小
            std::uint16_t width       = (bottomRight - topLeft).x + 1;
            std::uint16_t height      = (bottomRight - topLeft).y + 1;
            std::uint32_t totalPixels = width * height;
            std::uint32_t bufferSize  = this->BufferSize;

            // 分块写入
            std::uint32_t remaining = totalPixels;
            while (remaining > 0) {
                auto chunkSize = std::min(remaining, bufferSize);

                // 使用 image[x,y] 获取每一位的像素值，然后写入缓冲区
                for (std::uint32_t i = 0; i < chunkSize; ++i) {
                    std::uint32_t x = (totalPixels - remaining + i) % width;
                    std::uint32_t y = (totalPixels - remaining + i) / width;

                    this->buffer[i] = image[x, y] ? this->color.value() : this->backColor.value();
                }

                writeBuffer(chunkSize);

                remaining -= chunkSize;
            }
        }

        template <std::uint16_t Width, std::uint16_t Height>
        void drawPicture(const ColorImage<Width, Height> &image, Point topLeft = Point::Origin())
        {
            // 计算并设置显示区域
            const auto bottomRight = Point(Width - 1, Height - 1) + topLeft;
            setDrawArea(topLeft, bottomRight);

            // 计算图片尺寸、缓冲区大小
            std::uint16_t width       = (bottomRight - topLeft).x + 1;
            std::uint16_t height      = (bottomRight - topLeft).y + 1;
            std::uint32_t totalPixels = width * height;
            std::uint32_t bufferSize  = this->BufferSize;

            // 分块写入
            std::uint32_t remaining = totalPixels;
            while (remaining > 0) {
                auto chunkSize = std::min(remaining, bufferSize);

                // 填充image对应区域到缓冲区
                auto copyFirst = image.data() + (totalPixels - remaining);
                auto copyLast  = image.data() + (totalPixels - remaining + chunkSize);

                std::copy(copyFirst,
                          copyLast,
                          this->buffer.begin());

                writeBuffer(chunkSize);

                remaining -= chunkSize;
            }
        }

    private:
        SPI_HandleTypeDef &hspi;
        std::uint16_t      width;
        std::uint16_t      height;

        // 片选控制引脚
        GPIO_TypeDef *csPort;
        std::uint16_t csPin;

        // 背光控制引脚
        GPIO_TypeDef *backLightPort;
        std::uint16_t backLightPin;

        // 数据/命令控制引脚
        GPIO_TypeDef *dcPort;
        std::uint16_t dcPin;

        RGB565           color;     // 当前画笔颜色
        RGB565           backColor; // 背景颜色
        DisplayDirection direction; // 显示方向
        Point            cursor;    // 当前光标位置,用于设置屏幕控制器的显存写入方式

        std::array<std::uint16_t, BufferSize> buffer;

        // 背光控制
        void backLightOn(void);
        void backLightOff(void);

        // SPI 模式相关函数
        void SPI_SetDataSize(std::uint32_t dataSize);
        void chipSelect(void);
        void chipDeselect(void);
        void setCommandMode(void);
        void setDataMode(void);

        // SPI 数据传输函数
        void writeCommand(std::uint8_t cmd);
        void writeData(std::uint8_t data);
        void writeData(const std::uint8_t *pData, std::uint16_t size);
        void writeData(std::uint16_t data);
        void writeData(const std::uint16_t *pData, std::uint16_t size);
        void writeBuffer(void);
        void writeBuffer(std::uint16_t size);
    };
}

#endif // LCD154_HPP__